#region Imported Namespaces


using System;
using System.Collections.ObjectModel;
using System.Diagnostics;


#endregion


namespace WtfManager.Framework
{
  ///<summary>
  /// A base class for framework <see cref="Module"/> services.
  ///</summary>
  public abstract class Service: FrameworkComponent
  {
    #region Fields


    private readonly DependenciesCollection _dependencies = new DependenciesCollection();
    private bool _isInitialized;
    private bool _isInitializing;


    #endregion


    #region Constructors/Finalizers


    protected Service()
    {
      _dependencies.IsDependencyServicesReadyChanged += IsDependencyServicesReadyChanged;
    }


    #endregion


    #region Properties/Indexers


    public ReadOnlyCollection<Type> Dependencies
    {
      get
      {
        return new ReadOnlyCollection<Type>(_dependencies);
      }
    }


    #endregion


    #region Methods


    private void IsDependencyServicesReadyChanged(object sender, EventArgs e)
    {
      if (State != FrameworkComponentState.Initialized && State != FrameworkComponentState.Initializing)
      {
        return;
      }

      if (_dependencies.IsDependencyServicesReady)
      {
        if (_isInitialized)
        {
          State = FrameworkComponentState.Initializing;
          return;
        }

        if (_isInitializing)
        {
          return;
        }

        FrameworkComponentState state = OnInitialize();

        if (state == FrameworkComponentState.Initializing)
        {
          _isInitializing = true;

          State = FrameworkComponentState.Initializing;
          return;
        }

        if (state == FrameworkComponentState.Initialized)
        {
          _isInitialized = true;
          State = state;
          return;
        }

        State = FrameworkComponentState.Error;
        _isInitialized = false;
        _isInitializing = false;

        return;
      }

      State = FrameworkComponentState.Initializing;
    }


    protected void AddDependency(Type serviceType)
    {
      if (IsLoaded)
      {
        throw new InvalidOperationException("The service was already loaded.");
      }

      _dependencies.Add(serviceType);
    }


    protected override sealed FrameworkComponentState InitializeComponent()
    {
      FrameworkComponentState state = InternalOnInitialize();

      if(state != FrameworkComponentState.Initialized)
      {
        return state;
      }

      if (_dependencies.IsDependencyServicesReady)
      {
        state = OnInitialize();

        if (state == FrameworkComponentState.Initializing)
        {
          _isInitializing = true;
          return state;
        }

        if (state == FrameworkComponentState.Initialized)
        {
          return state;
        }

        return FrameworkComponentState.Error;
      }

      return FrameworkComponentState.Initializing;
    }


    internal virtual FrameworkComponentState InternalOnInitialize()
    {
      return FrameworkComponentState.Initialized;
    }


    protected abstract FrameworkComponentState OnInitialize();


    protected void OnInitializationComplete()
    {
      if (State != FrameworkComponentState.Initializing)
      {
        State = FrameworkComponentState.Error;

        throw new InvalidOperationException("Service was not in Initializing state.");
      }

      _isInitializing = false;
      _isInitialized = true;
      State = FrameworkComponentState.Initialized;
    }


    protected sealed override FrameworkComponentState LoadComponent()
    {
      FrameworkComponentState state = OnLoad();

      if (state == FrameworkComponentState.Loading)
      {
        Debug.Fail("FrameworkComponentState.Loading state is not yet implemented.");
        throw new InvalidOperationException("FrameworkComponentState.Loading state is not yet implemented.");
      }

      if (state == FrameworkComponentState.Loaded)
      {
        return state;
      }

      return FrameworkComponentState.Error;
    }


    protected abstract FrameworkComponentState OnLoad();


    protected void OnLoadingComplete()
    {
      if (State != FrameworkComponentState.Loading)
      {
        State = FrameworkComponentState.Error;

        throw new InvalidOperationException("Service was not in Loading state.");
      }

      State = FrameworkComponentState.Loaded;
    }


    #endregion
  }
}